﻿// -----------------------------------------------------------------------
// <copyright file="ClientSession.cs" company="FastNET Services">
//     Copyright (c) 2008-2009. All Rights Reserved.
// </copyright>
// <license>
//     GPLv2
// </license>
// <warranty>
//     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
//     BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
//     DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </warranty>
// -----------------------------------------------------------------------
#region Usings

using System;
using System.Security.Cryptography;
using GateShift.Servers.GameServer.Properties;
using GateShift.Services.SDK.Services;
using GateShift.Services.SDK.Services.Hosts;
using GateShift.Services.SDK.Threading;

#endregion

namespace GateShift.Servers.GameServer.Managers.Session
{
    /// <summary>
    /// Class for managing a single client session
    /// </summary>
    internal class ClientSession : IClientSession
    {
        #region Variables

        private readonly ISessionHost _host;
        private readonly Guid _sessionID; // No autoproperty. This should never change after ctor
        private readonly DateTime _startTime; // No autoproperty. This should never change after ctor

        #endregion

        #region CTor/DTor

        /// <summary>
        /// Initializes a new instance of the ClientSession class
        /// </summary>
        /// <param name="host">ISessionHost used for session management</param>
        public ClientSession(ISessionHost host)
        {
            _host = host;
            _sessionID = Guid.NewGuid(); // No autoproperty. This should never change after ctor
            _startTime = DateTime.UtcNow; // No autoproperty. This should never change after ctor
            Properties = new ThreadedDictionary<string, object>();
            TokenExpirationDate = DateTime.UtcNow;
        }

        #endregion

        #region Events

        /// <summary>
        /// Event raised when the token has been renewed
        /// </summary>
        public event EventHandler TokenRenewed;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the ID of the session
        /// </summary>
        /// <remarks>No autoproperty. This should never change after ctor</remarks>
        public Guid SessionID
        {
            get
            {
                return _sessionID;
            }
        }

        /// <summary>
        /// Gets the total time the user has been online during this session
        /// </summary>
        public TimeSpan SessionTime
        {
            get
            {
                return DateTime.UtcNow.Subtract(_startTime);
            }
        }

        /// <summary>
        /// Gets a dictionary of session properties for this session
        /// </summary>
        public ThreadedDictionary<string, object> Properties { get; private set; }

        /// <summary>
        /// Gets a value indicating whether or not this user is logged into the system currently
        /// </summary>
        public bool IsLoggedIn
        {
            get
            {
                return SessionToken != null;
            }
        }

        /// <summary>
        /// Gets a value indicating whether or not the authentication token has expired
        /// </summary>
        public bool IsTokenExpired
        {
            get
            {
                return TokenExpirationLifeTime <= TimeSpan.Zero;
            }
        }

        /// <summary>
        /// Gets the date the authentication token will expire
        /// </summary>
        public DateTime TokenExpirationDate { get; private set; }

        /// <summary>
        /// Gets the time left for this session's authentication token
        /// </summary>
        public TimeSpan TokenExpirationLifeTime
        {
            get
            {
                return TokenExpirationDate.Subtract(DateTime.UtcNow);
            }
        }

        /// <summary>
        /// Gets the current authentication token for this session.
        /// Null signifies the session is not authenticated
        /// </summary>
        public byte[] SessionToken { get; private set; }

        #endregion

        #region Authentication Based

        /// <summary>
        /// Creates or renews the authentication token for this session
        /// </summary>
        public void RenewSessionToken()
        {
            SessionToken = new byte[32];
            RandomNumberGenerator.Create().GetBytes(SessionToken);
            TokenExpirationDate = DateTime.UtcNow.Add(Settings.Default.SessionTokenLeaseTime);
            OnTokenRenewed();
        }

        /// <summary>
        /// Clears the authentication token for this session
        /// </summary>
        public void ClearSessionToken()
        {
            SessionToken = null;
            ExpireSessionToken();
        }

        /// <summary>
        /// Expires the authentication token for this session
        /// </summary>
        public void ExpireSessionToken()
        {
            TokenExpirationDate = DateTime.UtcNow;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Disconnects this client
        /// </summary>
        public void Disconnect()
        {
            if (_host != null)
            {
                _host.DisconnectClient(_sessionID);
            }
        }

        /// <summary>
        /// Sends a set of data to the client
        /// </summary>
        /// <param name="serviceID">Client Service ID to talk to</param>
        /// <param name="serviceCode">Client Service Code to send</param>
        /// <param name="data">Serialized Data for the client</param>
        /// <param name="encrypt">Whether or not to encrypt the data to the client</param>
        public void SendData(byte serviceID, byte serviceCode, byte[] data, bool encrypt)
        {
            if (_host != null)
            {
                _host.SendClientData(_sessionID, serviceID, serviceCode, data, encrypt);
            }
        }

        #endregion

        #region Event Raisers

        /// <summary>
        /// Event raiser for TokenRenewed
        /// </summary>
        protected void OnTokenRenewed()
        {
            if (TokenRenewed != null)
            {
                TokenRenewed(this, EventArgs.Empty);
            }
        }

        #endregion
    }
}